# Copyright (c) 2011 Juan Manuel Borges Caño

# Objects naming: freely as *the* model, as "Tag.Name" for tag "Name".
# The model and the tags have to share the same armature.

# Order Note: Bones, Vertices, Textures, Materials, Faces, Tags, Weights, Frames, Actions

# os.path.basename(filename)

import bpy, mathutils, math

def export_jmbcmodel(context, filepath, use_some_setting):

	stream = open(filepath, 'w')

	scene = bpy.context.scene
	world = scene.world
	model = bpy.context.active_object
	tags = []
	for object in bpy.context.selected_objects:
		if object.name[0:4] == "Tag.":
			tags.append(object)

	armature = model.find_armature()

	bonemap = {}
	for bone in range(len(armature.data.bones)):
		bonemap[armature.data.bones[bone].name] = bone

	# Bones
	
	start = 0
	stream.write('bones %s\n' % len(armature.data.bones))
	for bone in armature.data.bones:
		parents = bone.parent_recursive
		stream.write('start %s\n' % start)
		stream.write('parents %s\n' % len(parents))
		start += len(parents)

	# Parents

	stream.write('parents %s\n' % start)
	for bone in armature.data.bones:
		parents = bone.parent_recursive
		for parent in parents:
			stream.write('bone %s\n' % bonemap[parent.name])

	# Vertices

	weights = 0
	stream.write('vertices %s\n' % len(model.data.vertices))
	for vertex in model.data.vertices:
		co = model.matrix_world * vertex.co
		no = model.matrix_world * vertex.normal
		stream.write('coord %s %s %s\n' % (co.x, co.y, co.z))
		stream.write('normal %s %s %s\n' % (no.x, no.y, no.z))
		stream.write('start %s\n' % weights)
		stream.write('weights %s\n' % len(vertex.groups))
		weights += len(vertex.groups)

	# Materials

	faces = []
	for material in model.data.materials:
		faces.append(0)
	for material in model.data.materials:
		for face, uvface in enumerate(model.data.uv_textures.active.data):
			if model.data.materials[model.data.faces[face].material_index] == material:
				faces[model.data.faces[face].material_index] += 1
	start = 0
	i = 0
	stream.write('materials %s\n' % len(model.data.materials))
	for material in model.data.materials:
		stream.write('name %s\n' % material.name)
		stream.write('start %s\n' % start)
		stream.write('faces %s\n' % faces[i])
		start += faces[i]
		i += 1

	# Faces

	stream.write('faces %s\n' % len(model.data.faces))
	for material in model.data.materials:
		for face, uvface in enumerate(model.data.uv_textures.active.data):
			if model.data.materials[model.data.faces[face].material_index] == material:
				stream.write('vertices')
				for vertex in model.data.faces[face].vertices:
					stream.write(' %s' % vertex)
				stream.write('\n')
				stream.write('normal')
				for normal in model.data.faces[face].normal:
					stream.write(' %s' % normal)
				stream.write('\n')
				stream.write('texcoords')
				for vertex in range(len(model.data.faces[face].vertices)):
					stream.write(' %s %s' % (uvface.uv[vertex][0], uvface.uv[vertex][1]))
				stream.write('\n')

	# Tags
	# A tag haves *one* vertex

	stream.write('tags %s\n' % len(tags))
	for tag in tags:
		stream.write('name %s\n' % tag.name[4:])
		vertex = tag.data.vertices[0]
		co = tag.matrix_world * vertex.co
		matrix = mathutils.Matrix() * 0.0
		for group in vertex.groups:
			matrix = matrix + (armature.data.bones[tag.vertex_groups[group.group].name].matrix_local * group.weight)
		matrix = mathutils.Matrix.Rotation(math.radians(180), 4, 'Y') * matrix * armature.matrix_world
		stream.write('matrix %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n' % (matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3], matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3], matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3], co[0], co[1], co[2], matrix[3][3]))  
		stream.write('start %s\n' % weights)
		stream.write('weights %s\n' % len(vertex.groups))
		weights += len(vertex.groups)

	# Weights

	stream.write('weights %s\n' % weights)
	for vertex in model.data.vertices:
		for group in vertex.groups:
			stream.write('weight %s %s\n' % (bonemap[model.vertex_groups[group.group].name], group.weight))
	for tag in tags:
		vertex = tag.data.vertices[0]
		for group in vertex.groups:
			stream.write('weight %s %s\n' % (bonemap[tag.vertex_groups[group.group].name], group.weight))

	# Frames

	actionbkp = armature.animation_data.action
	framebkp = scene.frame_current
	frames = 0
	for nla_track in armature.animation_data.nla_tracks:
		for strip in nla_track.strips:
			frames += int(strip.action_frame_end - strip.action_frame_start) + 1
	stream.write('frames %s\n' % (frames * len(armature.data.bones)))
	for nla_track in armature.animation_data.nla_tracks:
		for strip in nla_track.strips:
			armature.animation_data.action = strip.action
			for frame in range(int(strip.action_frame_start), int(strip.action_frame_end) + 1):
				scene.frame_set(frame)
				for bone in armature.pose.bones:
					matrix = bone.matrix * armature.data.bones[bone.name].matrix_local.inverted() * armature.matrix_world
					stream.write('matrix %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n' % (matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3], matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3], matrix[2][0], matrix[2][1], matrix[2][2],  matrix[2][3],  matrix[3][0],  matrix[3][1],  matrix[3][2],  matrix[3][3]))  
	armature.animation_data.action = actionbkp
	scene.frame_set(framebkp)

	# Actions

	actions = 0
	for nla_track in armature.animation_data.nla_tracks:
		actions += len(nla_track.strips)
	stream.write('actions %s\n' % actions)
	frame = 0
	for nla_track in armature.animation_data.nla_tracks:
		for strip in nla_track.strips:
			stream.write('name %s\n' % strip.action.name)
			stream.write('start %s\n' % frame)
			frames = int(strip.action_frame_end - strip.action_frame_start) + 1
			stream.write('frames %s\n' % frames)
			frame += frames

	stream.close()

	return {'FINISHED'}


from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty


class ExportJMBCModel(bpy.types.Operator, ExportHelper):
	'''Export to juanmabc Model format.'''

	bl_idname = "export.model"
	bl_label = "Export juanmabc Model"
	filename_ext = ".model"
	filter_glob = StringProperty(default="*.model", options={'HIDDEN'})

	@classmethod
	def poll(cls, context):
		return context.active_object != None

	def execute(self, context):
		return export_jmbcmodel(context, self.filepath, None)


def menu_func_export(self, context):
	self.layout.operator(ExportJMBCModel.bl_idname, text="Export to juanmabc Model format.")


def register():
	bpy.utils.register_class(ExportJMBCModel)
	bpy.types.INFO_MT_file_export.append(menu_func_export)


def unregister():
	bpy.utils.unregister_class(ExportJMBCModel)
	bpy.types.INFO_MT_file_export.remove(menu_func_export)


if __name__ == "__main__":
	register()
	bpy.ops.export.model('INVOKE_DEFAULT')
